﻿using System;
using System.Threading;
using System.Collections.Generic;

namespace JN.Utility 
{
    public class TimerController
    {
        private static TimerController _instance;
        private static readonly object _uniqueObj = new object();

        public static TimerController Instance
        {
            get
            {
                if (_instance == null)
                {
                    lock (_uniqueObj)
                    {
                        if (_instance == null)
                        {
                            _instance = new TimerController();
                        }
                    }
                }
                return _instance;
            }
        }

        private TimerController()
        {
            _timer.Elapsed += new System.Timers.ElapsedEventHandler(_timer_Elapsed);
            _timer.Interval = TimerInterval;
            _timer.Stop();
        }

        private readonly int TimerInterval = 200; //毫秒.
        private System.Timers.Timer _timer = new System.Timers.Timer();
        private Dictionary<string, TimerItem> _timerItemContainer = new Dictionary<string, TimerItem>();
        private List<string> _timerItemNameList = new List<string>();
        private Dictionary<string, TimerItemRunStatus> _runStatusContainer = new Dictionary<string, TimerItemRunStatus>();
        private static readonly object _syncRoot = new object();
        private static readonly object _timerLock = new object();


        private bool _isBusy = false;
        private void _timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            try
            {
                if (!_isBusy)
                {
                    _isBusy = true;
                    lock (_timerLock)
                    {
                        bool isAllStop = true;
                        lock (_syncRoot)
                        {
                            _timerItemNameList.Clear();
                            foreach (TimerItem tempItem in _timerItemContainer.Values)
                            {
                                _timerItemNameList.Add(tempItem.ItemName);
                            }
                        }
                        foreach (string itemName in _timerItemNameList)
                        {
                            lock (_syncRoot)
                            {
                                TimerItem item = null;
                                if (_runStatusContainer.ContainsKey(itemName) && _timerItemContainer.ContainsKey(itemName))
                                {
                                    item = _timerItemContainer[itemName];
                                    TimerItemRunStatus itemRunStatus = _runStatusContainer[itemName];
                                    if (itemRunStatus.IsRunning)
                                    {
                                        isAllStop = false;
                                        int elapse = 0;
                                        DateTime current = DateTime.Now;
                                        TimeSpan span = (current - itemRunStatus.CalcStartTime);
                                        itemRunStatus.CalcStartTime = current;
                                        if (span.Ticks > 0)
                                        {
                                            elapse = (int)(span.Ticks / 10000);
                                        }

                                        itemRunStatus.Remains -= elapse; // TimerInterval;
                                        if (itemRunStatus.Remains <= 0)
                                        {
                                            itemRunStatus.IsRunning = false;
                                            itemRunStatus.IsStarted = false;
                                            itemRunStatus.Remains = item.Interval; //恢复计时事件的剩余时间.

                                            if (item.TimeOutHandler != null)
                                            {
                                                ThreadPool.QueueUserWorkItem(new WaitCallback(delegate(object sender2)
                                                {
                                                    try
                                                    {
                                                        item.TimeOutHandler(item.ItemName);
                                                    }
                                                    catch
                                                    {

                                                    }
                                                }));

                                                //("TimeOut:" + item.ItemName + ",Interval " + item.Interval
                                                //    + " StartTime:" + itemRunStatus.StartTime.ToString("yyyy-MM-dd HH:mm:ss.fff")
                                                //    + " TimeOutTime:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"));
                                            }
                                        }
                                    }
                                }
                            }
                        }//foreach

                        if (isAllStop == true)
                        {
                            _timer.Stop();
                        }
                    }
                    _isBusy = false;
                }
            }
            catch
            {

            }
        }

        /// <summary>
        /// 添加计时事件.
        /// </summary>
        /// <param name="itemName">计时事件名称</param>
        /// <param name="interval">超时间隔(毫秒)</param>
        /// <param name="timeOutHandler">超时后被回调的委托</param>
        public void AddTimerItem(string itemName, int interval, TimeOutHandlerDelegate timeOutHandler)
        {
            lock (_syncRoot)
            {
                if (itemName == null || itemName.Length == 0)
                {
                    throw new ArgumentException("itemName");
                }
                if (_timerItemContainer.ContainsKey(itemName)) //如果存在则重新注册.
                {
                    _timerItemContainer.Remove(itemName);
                    _runStatusContainer.Remove(itemName);
                    //throw new ArgumentException("TimerItem [" + itemName + "] is registered.");
                }
                TimerItem item = new TimerItem(itemName, interval, timeOutHandler);
                _timerItemContainer.Add(itemName, item);
                _runStatusContainer.Add(itemName, new TimerItemRunStatus(item));
            }
        }

        public void UpdateTimerInterval(string itemName, int interval)
        {
            lock (_syncRoot)
            {
                if (!string.IsNullOrEmpty(itemName) && interval > 0)
                {
                    if (_timerItemContainer.ContainsKey(itemName))
                    {
                        _timerItemContainer[itemName].Interval = interval;
                    }
                }
            }
        }

        /// <summary>
        /// remove计时事件.
        /// </summary>
        /// <param name="itemName">计时事件名称</param>
        /// <param name="interval">超时间隔(毫秒)</param>
        /// <param name="timeOutHandler">超时后被回调的委托</param>
        public void RemoveTimerItem(string itemName)
        {
            lock (_syncRoot)
            {
                if (itemName == null || itemName.Length == 0)
                {
                    throw new ArgumentException("itemName");
                }
                if (_timerItemContainer.ContainsKey(itemName))
                {
                    _timerItemContainer.Remove(itemName);
                    _runStatusContainer.Remove(itemName);
                }
            }
        }

        /// <summary>
        /// 添加计时事件.
        /// </summary>
        /// <param name="item"></param>
        public void AddTimerItem(TimerItem item)
        {
            lock (_syncRoot)
            {
                if (_timerItemContainer.ContainsKey(item.ItemName))
                {
                    throw new ArgumentException("TimerItem [" + item.ItemName + "] is registered.");
                }
                _timerItemContainer.Add(item.ItemName, item);
                _runStatusContainer.Add(item.ItemName, new TimerItemRunStatus(item));
            }
        }

        /// <summary>
        /// 启动指定的计时事件. 
        /// </summary>
        /// <param name="itemName"></param>
        public void StartTimerItem(string itemName)
        {
            lock (_syncRoot)
            {
                if (_timerItemContainer.ContainsKey(itemName))
                {
                    if (_runStatusContainer[itemName].IsStarted == false) //第一次启动
                    {
                        _runStatusContainer[itemName].StartTime = DateTime.Now;
                        _runStatusContainer[itemName].CalcStartTime = _runStatusContainer[itemName].StartTime;
                        _runStatusContainer[itemName].IsRunning = true;
                        _runStatusContainer[itemName].IsStarted = true;
                    }
                    else if (_runStatusContainer[itemName].IsRunning == false) //暂停后重新启动.
                    {
                        _runStatusContainer[itemName].IsRunning = true;
                        _runStatusContainer[itemName].CalcStartTime = DateTime.Now;
                    }
                }
                if (!_timer.Enabled)
                {
                    _timer.Start();
                }
            }
        }

        /// <summary>
        /// 暂停指定的计时事件.
        /// </summary>
        /// <param name="itemName"></param>
        public void PauseTimerItem(string itemName)
        {
            lock (_syncRoot)
            {
                if (_timerItemContainer.ContainsKey(itemName))
                {
                    _runStatusContainer[itemName].IsRunning = false;
                    int elapse = 0;
                    DateTime current = DateTime.Now;
                    TimeSpan span = (current - _runStatusContainer[itemName].CalcStartTime);
                    _runStatusContainer[itemName].CalcStartTime = current;
                    if (span.Ticks > 0)
                    {
                        elapse = (int)(span.Ticks / 10000);
                    }
                    _runStatusContainer[itemName].Remains -= elapse; // TimerInterval;
                }
            }
        }

        /// <summary>
        /// 停止指定的计时事件,且剩余的计时时间恢复为该计时事件超时间隔.
        /// </summary>
        /// <param name="itemName"></param>
        public void StopTimerItem(string itemName)
        {
            lock (_syncRoot)
            {
                if (_timerItemContainer.ContainsKey(itemName))
                {
                    TimerItemRunStatus item = _runStatusContainer[itemName];
                    item.IsRunning = false;
                    item.IsStarted = false;
                    item.Remains = _timerItemContainer[itemName].Interval;
                }
            }
        }

        /// <summary>
        /// 获取指定的计时事件.
        /// </summary>
        /// <param name="itemName"></param>
        /// <returns></returns>
        public TimerItem GetTimerItem(string itemName)
        {
            lock (_syncRoot)
            {
                TimerItem timerItemByName = null;
                if (_timerItemContainer.ContainsKey(itemName))
                {
                    timerItemByName = _timerItemContainer[itemName];
                }
                return timerItemByName;
            }
        }

        private class TimerItemRunStatus
        {
            private string _itemName;
            private int _remains;   //本次计时还余下多少时间 毫秒.
            private bool _isRunning = false; //false 没有计时(暂停或还没有启动.),true 正在计时.                        
            private DateTime _startTime; //记录启动该定时项目时的时间，用于调试.如果中间发生暂停,则为最后一次启动该计时项目的时间.
            private DateTime _calcStartTime;
            private bool _isStarted = false;

            public TimerItemRunStatus(TimerItem item)
            {
                _itemName = item.ItemName;
                _remains = item.Interval;
                _isRunning = false;
            }

            public string ItemName
            {
                get { return _itemName; }
            }

            public int Remains
            {
                get { return _remains; }
                set { _remains = value; }
            }

            public bool IsRunning
            {
                get { return _isRunning; }
                set { _isRunning = value; }
            }

            public DateTime StartTime
            {
                get { return _startTime; }
                set { _startTime = value; }
            }

            public DateTime CalcStartTime
            {
                get { return _calcStartTime; }
                set { _calcStartTime = value; }
            }

            public bool IsStarted
            {
                get { return _isStarted; }
                set { _isStarted = value; }
            }

        }

    }

    public delegate void TimeOutHandlerDelegate(string timerItemName);

    public class TimerItem
    {
        private string _itemName;
        private int _interval; //计时间隔 毫秒.        
        private TimeOutHandlerDelegate _timeOutHandler;

        public TimerItem(string itemName, int interval, TimeOutHandlerDelegate timeOutHandler)
        {
            if (itemName == null || itemName.Length == 0)
            {
                throw new ArgumentException("itemName == null || itemName.Length == 0");
            }
            if (interval <= 0)
            {
                throw new ArgumentException("interval <= 0");
            }
            _itemName = itemName;
            _interval = interval;
            _timeOutHandler = timeOutHandler;
        }

        public string ItemName
        {
            get { return _itemName; }
        }

        public int Interval
        {
            get { return _interval; }
            set
            {
                if (value <= 0)
                {
                    throw new ArgumentException("value <= 0");
                }
                _interval = value;
            }
        }

        public TimeOutHandlerDelegate TimeOutHandler
        {
            get { return _timeOutHandler; }
            set { _timeOutHandler = value; }
        }

    }

}
